package bleudp

import (
	"context"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"iot-ble-server/dgram"
	"iot-ble-server/global/globalconstants"
	"iot-ble-server/global/globallogger"
	"iot-ble-server/global/globalmemo"
	"iot-ble-server/global/globalredis"
	"iot-ble-server/global/globalutils"
	"iot-ble-server/internal/config"
	"iot-ble-server/internal/packets"
	"iot-ble-server/internal/storage"
	"strconv"
	"time"

	"github.com/redis/go-redis/v9"
)

//验证 `H3C` 报文,报文构造错误
func checkMsgSafe(data []byte) bool {
	if len(data) < 4 {
		return false
	}
	messageLen, _ := strconv.ParseUint(hex.EncodeToString(append(data[:0:0], data[2:4]...)), 16, 16)
	return int(messageLen) == len(data)
}

//上行 `UDP` 消息解析
func parseUdpMessage(data []byte, rinfo dgram.RInfo) (packets.JsonUdpInfo, error) {
	var err error
	offset, jsonUdpInfo, dataLen := 0, packets.JsonUdpInfo{}, len(data)
	jsonUdpInfo.Rinfo = rinfo
	jsonUdpInfo.MessageHeader = ParseMessageHeader(data, &offset)
	if globalutils.JudgePacketLenthLimit(offset, dataLen) {
		return jsonUdpInfo, nil
	}
	jsonUdpInfo.MessageBody, err = ParseMessageBody(data, jsonUdpInfo.MessageHeader.LinkMsgType, &offset)
	if err != nil || globalutils.JudgePacketLenthLimit(offset, dataLen) {
		return jsonUdpInfo, err
	}
	jsonUdpInfo.MessageAppHeader = ParseMessageAppHeader(data, &offset)
	if globalutils.JudgePacketLenthLimit(offset, dataLen) {
		return jsonUdpInfo, nil
	}
	jsonUdpInfo.MessageAppBody, err = ParseMessageAppBody(data, &offset, jsonUdpInfo.MessageBody.GwMac, jsonUdpInfo.MessageAppHeader.Type)
	if err != nil {
		return jsonUdpInfo, err
	}
	return jsonUdpInfo, nil
}

func ParseMessageHeader(data []byte, offset *int) packets.MessageHeader {
	*offset = 12
	return packets.MessageHeader{
		Version:           hex.EncodeToString(append(data[:0:0], data[0:2]...)),
		LinkMessageLength: hex.EncodeToString(append(data[:0:0], data[2:4]...)),
		LinkMsgFrameSN:    hex.EncodeToString(append(data[:0:0], data[4:8]...)),
		LinkMsgType:       hex.EncodeToString(append(data[:0:0], data[8:10]...)),
		OpType:            hex.EncodeToString(append(data[:0:0], data[10:12]...)),
	}
}

func ParseMessageAppHeader(data []byte, offset *int) packets.MessageAppHeader {
	tempOffset := *offset
	*offset += 12
	return packets.MessageAppHeader{
		TotalLen:   hex.EncodeToString(append(data[:0:0], data[tempOffset:tempOffset+2]...)),
		SN:         hex.EncodeToString(append(data[:0:0], data[tempOffset+2:tempOffset+4]...)),
		CtrlField:  hex.EncodeToString(append(data[:0:0], data[tempOffset+4:tempOffset+6]...)),
		FragOffset: hex.EncodeToString(append(data[:0:0], data[tempOffset+6:tempOffset+8]...)),
		Type:       hex.EncodeToString(append(data[:0:0], data[tempOffset+8:tempOffset+10]...)),
		OpType:     hex.EncodeToString(append(data[:0:0], data[tempOffset+10:tempOffset+12]...)),
	}
}

//目前仅网关管理、BLE终端存在Body部分，链路消息无
func ParseMessageBody(data []byte, msgType string, offset *int) (packets.MessageBody, error) {
	var msgBody = packets.MessageBody{}
	var err error
	msgBody.GwMac = hex.EncodeToString(append(data[:0:0], data[*offset:*offset+6]...))
	switch msgType {
	case packets.Hello:
		msgBody.GwAbility = hex.EncodeToString(append(data[:0:0], data[*offset+6:*offset+8]...))
		msgBody.ServiceType = hex.EncodeToString(append(data[:0:0], data[*offset+8:*offset+9]...))
		msgBody.InitMarked = hex.EncodeToString(append(data[:0:0], data[*offset+9:*offset+10]...))
		msgBody.Reserve = hex.EncodeToString(append(data[:0:0], data[*offset+10:*offset+12]...))
		*offset += 12
	case packets.IotModuleStatusChange:
		msgBody.TLV.TLVMsgType = hex.EncodeToString(append(data[:0:0], data[*offset+6:*offset+8]...))
		msgBody.TLV.TLVLen = hex.EncodeToString(append(data[:0:0], data[*offset+8:*offset+10]...))
		msgBody.TLV.TLVPayload = packets.TLVFeature{
			IotModuleId:           hex.EncodeToString(append(data[:0:0], data[*offset+10:*offset+12]...)),
			IotModuleStatus:       hex.EncodeToString(append(data[:0:0], data[*offset+12:*offset+13]...)),
			IotModuleChangeReason: hex.EncodeToString(append(data[:0:0], data[*offset+13:*offset+14]...)),
		}
		*offset += 14
	case packets.GatewayDevInfo:
		msgBody.ErrorCode = hex.EncodeToString(append(data[:0:0], data[*offset+6:*offset+8]...))
		if err := globalutils.VerificationErrorCode(msgBody.ErrorCode); err != nil {
			return packets.MessageBody{}, err
		}
		msgBody.TLV.TLVMsgType = hex.EncodeToString(append(data[:0:0], data[*offset+8:*offset+10]...))
		msgBody.TLV.TLVLen = hex.EncodeToString(append(data[:0:0], data[*offset+10:*offset+12]...))
		msgBody.TLV.TLVPayload.IotModuleId = hex.EncodeToString(append(data[:0:0], data[*offset+12:*offset+14]...))
		msgBody.TLV.TLVPayload.Slot = hex.EncodeToString(append(data[:0:0], data[*offset+14:*offset+15]...))
		msgBody.TLV.TLVPayload.ReserveOne = hex.EncodeToString(append(data[:0:0], data[*offset+15:*offset+16]...))
		msgBody.TLV.TLVPayload.TLVReserve = make([]packets.TLV, 0)
		dfsForAllTLV(data, &msgBody.TLV.TLVPayload.TLVReserve, *offset+16, len(data))
		*offset = len(data)
	case packets.IotModuleRset:
		msgBody.ModuleID = hex.EncodeToString(append(data[:0:0], data[*offset+6:*offset+8]...))
		msgBody.ErrorCode = hex.EncodeToString(append(data[:0:0], data[*offset+8:*offset+10]...))
		*offset += 10
	case packets.GerenalResponse:
		msgBody.ErrorCode = hex.EncodeToString(append(data[:0:0], data[*offset+6:*offset+8]...))
		msgBody.WaitConfirmMsg = hex.EncodeToString(append(data[:0:0], data[*offset+8:*offset+10]...))
		*offset += 10
	default:
		msgBody.ModuleID = hex.EncodeToString(append(data[:0:0], data[*offset+6:*offset+8]...))
		if msgType[0:2] != packets.TerminalManager {
			err = errors.New("ParseMessageBody: from: " + msgBody.GwMac + " unable to recognize message type with " + msgType)
		}
		*offset += 8
	}
	return msgBody, err
}

func ParseMessageAppBody(data []byte, offset *int, devMac string, msgType string) (packets.MessageAppBody, error) {
	var err error
	parseMessageAppBody := packets.MessageAppBody{}
	switch msgType {
	case packets.BleConfirm:
		parseMessageAppBody.ErrorCode = hex.EncodeToString(append(data[:0:0], data[*offset:*offset+2]...))
		parseMessageAppBody.RespondFrame = hex.EncodeToString(append(data[:0:0], data[*offset+2:*offset+6]...))
	case packets.BleResponse:
		parseMessageAppBody.ErrorCode = hex.EncodeToString(append(data[:0:0], data[*offset:*offset+2]...))
		parseMessageAppBody.RespondFrame = hex.EncodeToString(append(data[:0:0], data[*offset+2:*offset+6]...))
		parseMessageAppBody.TLV, err = GetDevResponseTLV(data, *offset+6)
		if err != nil {
			return packets.MessageAppBody{}, err
		}
	case packets.BleGetConnDevList:
		parseMessageAppBody.ErrorCode = hex.EncodeToString(append(data[:0:0], data[*offset:*offset+2]...))
		parseMessageAppBody.DevSum = hex.EncodeToString(append(data[:0:0], data[*offset+2:*offset+4]...))
		parseMessageAppBody.Reserve = hex.EncodeToString(append(data[:0:0], data[*offset+4:*offset+6]...))
		parseMessageAppBody.TLV, err = GetDevResponseTLV(data, *offset+6)
		if err != nil {
			return packets.MessageAppBody{}, err
		}
	case packets.BleCharacteristicNotice:
		parseMessageAppBody.TLV, err = GetDevResponseTLV(data, *offset)
		if err != nil {
			return packets.MessageAppBody{}, err
		}
	case packets.BleBoardcast, packets.BleTerminalEvent:
		parseMessageAppBody.MultiTLV = make([]packets.TLV, 0)
		err = dfsForAllTLV(data, &parseMessageAppBody.MultiTLV, *offset, len(data))
		if err != nil {
			return packets.MessageAppBody{}, err
		}
	default:
		err = errors.New("ParseMessageAppBody: from: " + devMac + " unable to recognize message type with " + parseMessageAppBody.TLV.TLVMsgType)
	}
	return parseMessageAppBody, err
}

// `frame` 帧序列 | `reSendTime` 发送次数 | `devEui` 唯一标识 标识消息队列 设备mac or 网关mac + module id | `curQueue` 消息队列 |
// func ResendMessage(ctx context.Context, frameSN, reSendTimes int, devMac, gwMac string, waitDown []byte) {
// 	if reSendTimes >= 3 {
// 		globallogger.Log.Errorf("<ResendMessage> : devEui: %s has issue with the current retransmission message device\n", devMac)
// 		return
// 	}
// 	timer := time.NewTimer((time.Second * time.Duration(config.C.General.KeepAliveTime)))
// 	defer timer.Stop()
// 	<-timer.C
// 	if config.C.General.UseRedis {
// 		byteNode, err := globalredis.RedisCache.LIndex(ctx, devMac, 0).Result()
// 		if err != nil {
// 			if err != redis.Nil {
// 				globallogger.Log.Errorf("<ResendMessage> :devEui:%s redis has error, %v\n", devMac, err)
// 			}
// 			return //空值就直接放弃
// 		}
// 		var curNode storage.NodeCache
// 		err = json.Unmarshal([]byte(byteNode), &curNode)
// 		if err != nil {
// 			globallogger.Log.Errorf("<ResendMessage> :devEui:%s decode has error, %v\n", devMac, err)
// 			return
// 		}
// 		globallogger.Log.Infof("<ResendMessage> : devEui: %s resend msg, the msg type is %s\n", devMac, curNode.MsgType)
// 		if frameSN == curNode.FrameSN {
// 			SendDownMessage(waitDown, devMac, gwMac)
// 			go ResendMessage(ctx, frameSN, reSendTimes+1, devMac, gwMac, waitDown)
// 		}
// 	} else {
// 		tempQueue, ok := globalmemo.MemoCacheDev.Get(devMac)
// 		if ok {
// 			curQueue := tempQueue.(*storage.CQueue)
// 			if curQueue.Len() > 0 && frameSN == curQueue.Peek().FrameSN {
// 				globallogger.Log.Warnf("<ResendMessage> : devEui: %s resend msg, the msg type is %s", devMac, curQueue.Peek().MsgType)
// 				SendDownMessage(waitDown, devMac, gwMac)
// 				go ResendMessage(ctx, frameSN, reSendTimes+1, devMac, gwMac, waitDown)
// 			}
// 		} //若不存在待下发的，就放弃重传操作
// 	}
// }

//异步方法， 自旋等待,在下发消息前，需要从currentMap中取到对应消息队列
//redis就传key下来 curQueue
//方法仅针对终端，因为网关应当不是一发一收
//终端消息 devEui = devMac | 网关与插卡 则是网关插卡与id
func SendMsgBeforeDown(ctx context.Context, sendBytes []byte, gwFrameSN, devFrameSN string, devEui, gwMac, moduleId, msgType string) error {
	if devEui != "" {
		globallogger.Log.Infof("<SendMsgBeforeDown> [DevEui: %s] the current data msg type is %s", devEui, msgType)
		markKey := globalutils.CreateCacheKey(globalconstants.BleDevCacheMessagePrefix, gwMac, moduleId, devFrameSN)
		curMemo := storage.NodeCache{
			FirstMark: markKey,
			TimeStamp: time.Now(),
			MsgType:   msgType,
		}
		cache, err := json.Marshal(curMemo)
		if err != nil {
			return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] data generate failed %v", devEui, err)
		}
		listCacheKey := globalutils.CreateCacheKey(globalconstants.BleDevCacheMessagePrefix, gwMac, moduleId)
		if config.C.General.UseRedis {
			preByte, err := globalredis.RedisCache.LIndex(ctx, listCacheKey, 0).Result()
			if err != nil {
				if err != redis.Nil {
					return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] redis can't work %v", devEui, err)
				}
			} else {
				//存在数据时
				var headNode storage.NodeCache
				err = json.Unmarshal([]byte(preByte), &headNode)
				if err != nil {
					return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] data has error %v, unmarshal failed", devEui, err)
				}
				if headNode.MsgType == msgType { //去重
					return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] Repeat msgType: %s", devEui, msgType)
				}
			} //不重复或者空
			_, err = globalredis.RedisCache.RPush(ctx, listCacheKey, cache).Result()
			if err != nil {
				return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] redis can't work %v", devEui, err)
			}
			for { //循环检测判断是否下发
				queueByte, err := globalredis.RedisCache.LIndex(ctx, listCacheKey, 0).Result()
				var headNode storage.NodeCache
				if err != nil {
					return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] redis has error %v, frameSN sendDown failed", devEui, err)
				}
				err = json.Unmarshal([]byte(queueByte), &headNode)
				if err != nil {
					return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] data has error %v, umarshal failed", devEui, err)
				}
				if headNode.FirstMark == markKey { //超时，整个队列扔
					if globalutils.CompareTimeIsExpire(headNode.TimeStamp, globalconstants.LimitMessageTime) {
						globalredis.RedisCache.Del(ctx, listCacheKey)
						return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] queue has timeOut, frameSN sendDown failed", devEui)
					} else {
						if !SendDownMessage(sendBytes, devEui, gwMac) { //此处不需要设置缓存来保证消息确认
							globalredis.RedisCache.Del(ctx, listCacheKey)
							return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] send down failed", devEui)
						}
						//暂时无消息重发，仅仅等待
						//go ResendMessage(ctx, frameSN, 0, devEui, gwMac, sendBytes)
					}
					break
				}
				globallogger.Log.Infof("<SendMsgBeforeDown> [DevEui: %s] wait to send down message, current frame is %v", devEui, devFrameSN)
				time.Sleep(time.Second) //自旋等待下发
			}
			//下发后写入缓存等待
			globalredis.RedisCache.Set(ctx, globalutils.CreateCacheKey(globalconstants.BleGwDevCacheMessagePrefix, gwMac, gwFrameSN), time.Now().Format((time.RFC3339)), -1)
		} else {
			var curQueue *storage.CQueue
			if tempQueue, ok := globalmemo.MemoCacheDev.Get(listCacheKey); ok {
				curQueue = tempQueue.(*storage.CQueue)
			} else {
				curQueue = storage.NewCQueue()
			}
			if curQueue.Peek().MsgType == msgType {
				return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] Repeat msgType: %s", devEui, msgType)
			}
			curQueue.Enqueue(curMemo)
			globalmemo.MemoCacheDev.Set(listCacheKey, curQueue)
			for {
				curQueue, ok := globalmemo.MemoCacheDev.Get(listCacheKey)
				if !ok { //无待发数据
					return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] memo has error, frameSN sendDown failed", devEui)
				}
				if curQueue.(*storage.CQueue).Peek().FirstMark == markKey { //等待下发超时
					if globalutils.CompareTimeIsExpire(curQueue.(*storage.CQueue).Peek().TimeStamp, globalconstants.LimitMessageTime) {
						globalmemo.MemoCacheDev.Remove(listCacheKey)
						return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] queue has timeout, frameSN sendDown failed", devEui)
					} else {
						if !SendDownMessage(sendBytes, listCacheKey, gwMac) {
							//curQueue.(*storage.CQueue).Dequeue()
							globalmemo.MemoCacheDev.Remove(listCacheKey)
							return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] send down failed", devEui)
						}
					}
					break
				}
				globallogger.Log.Infof("<SendMsgBeforeDown> [DevEui: %s] wait to send down message, current frame is %v", devEui, devFrameSN)
				time.Sleep(time.Second)
			}
			globalmemo.MemoCacheGw.Set(globalutils.CreateCacheKey(globalconstants.BleGwDevCacheMessagePrefix, gwMac, gwFrameSN), time.Now())
		}
	} else { //网关主要消息处理
		globallogger.Log.Infof("<SendMsgBeforeDown> [DevEui: %s] the current data msg type is %s", gwMac, msgType)
		cacheKey := globalutils.CreateCacheKey(globalconstants.BleGwDevCacheMessagePrefix, gwMac, gwFrameSN)
		if SendDownMessage(sendBytes, devEui, gwMac) {
			markTime := time.Now()
			if config.C.General.UseRedis {
				_, err := globalredis.RedisCache.Set(ctx, cacheKey, markTime, -1).Result()
				if err != nil {
					return fmt.Errorf("<SendMsgBeforeDown> [DevEui: %s] redis has error %v", devEui, err)
				}
			} else {
				globalmemo.MemoCacheGw.Set(cacheKey, markTime)
			}
		}
	}
	return nil
}

//`搜TLV`
func dfsForAllTLV(data []byte, restore *[]packets.TLV, index int, limitLen int) error {
	var err error
	for i := index; i < limitLen; {
		tempTLV := packets.TLV{
			TLVMsgType: hex.EncodeToString(append(data[:0:0], data[i:i+2]...)),
			TLVLen:     hex.EncodeToString(append(data[:0:0], data[i+2:i+4]...)),
		}
		tempLen, _ := strconv.ParseUint(tempTLV.TLVLen, 16, 16)
		switch tempTLV.TLVMsgType {
		case packets.TLVGatewayDescribeMsg:
			tempTLV.TLVPayload.TLVReserve = make([]packets.TLV, 0)
			err = dfsForAllTLV(data, &tempTLV.TLVPayload.TLVReserve, i+4, i+int(tempLen))
			if err != nil {
				return err
			}
		case packets.TLVIotModuleEventMsg:
			tempTLV.TLVPayload.Event = hex.EncodeToString(append(data[:0:0], data[i+4:i+5]...))
			tempTLV.TLVPayload.ReserveOne = hex.EncodeToString(append(data[:0:0], data[i+5:i+6]...))
			tempTLV.TLVPayload.IotModuleId = hex.EncodeToString(append(data[:0:0], data[i+6:i+8]...))
			err = dfsForAllTLV(data, &tempTLV.TLVPayload.TLVReserve, i+8, i+int(tempLen))
			if err != nil {
				return err
			}
		case packets.TLVServiceMsg:
			tempTLV.TLVPayload.Primary = hex.EncodeToString(append(data[:0:0], data[i+4:i+5]...))
			tempTLV.TLVPayload.ReserveOne = hex.EncodeToString(append(data[:0:0], data[i+5:i+6]...))
			tempTLV.TLVPayload.ServiceHandle = hex.EncodeToString(append(data[:0:0], data[i+6:i+8]...))
			tempTLV.TLVPayload.ServiceUUID = hex.EncodeToString(append(data[:0:0], data[i+8:i+int(tempLen)]...))
		case packets.TLVCharacteristicMsg:
			tempTLV.TLVPayload.Properties = hex.EncodeToString(append(data[:0:0], data[i+4:i+5]...))
			tempTLV.TLVPayload.ReserveOne = hex.EncodeToString(append(data[:0:0], data[i+5:i+6]...))
			tempTLV.TLVPayload.ServiceHandle = hex.EncodeToString(append(data[:0:0], data[i+6:i+8]...))
			tempTLV.TLVPayload.CharHandle = hex.EncodeToString(append(data[:0:0], data[i+8:i+10]...))
			tempTLV.TLVPayload.CharacterUUID = hex.EncodeToString(append(data[:0:0], data[i+10:i+int(tempLen)]...))
		case packets.TLVCharFindReqMsg:
			tempTLV.TLVPayload.Properties = hex.EncodeToString(append(data[:0:0], data[i+4:i+5]...))
			tempTLV.TLVPayload.ReserveOne = hex.EncodeToString(append(data[:0:0], data[i+5:i+6]...))
			tempTLV.TLVPayload.ServiceHandle = hex.EncodeToString(append(data[:0:0], data[i+6:i+8]...))
			tempTLV.TLVPayload.CharHandle = hex.EncodeToString(append(data[:0:0], data[i+8:i+10]...))
			tempTLV.TLVPayload.CharacterUUID = hex.EncodeToString(append(data[:0:0], data[i+10:]...))
		case packets.TLVCharDescribeMsg:
			tempTLV.TLVPayload.ServiceHandle = hex.EncodeToString(append(data[:0:0], data[i+4:i+6]...))
			tempTLV.TLVPayload.CharHandle = hex.EncodeToString(append(data[:0:0], data[i+6:i+8]...))
			tempTLV.TLVPayload.CCCDHandle = hex.EncodeToString(append(data[:0:0], data[i+8:i+10]...))
			tempTLV.TLVPayload.CharacterUUID = hex.EncodeToString(append(data[:0:0], data[i+10:i+int(tempLen)]...))
		case packets.TLVBroadcastMsg:
			tempTLV.TLVPayload.ReserveOne = hex.EncodeToString(append(data[:0:0], data[i+4:i+5]...))
			tempTLV.TLVPayload.AddrType = hex.EncodeToString(append(data[:0:0], data[i+5:i+6]...))
			tempTLV.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[i+6:i+12]...))
			tempTLV.TLVPayload.RSSI = hex.EncodeToString(append(data[:0:0], data[i+12:i+13]...))
			tempTLV.TLVPayload.ADType = hex.EncodeToString(append(data[:0:0], data[i+13:i+14]...))
			tempTLV.TLVPayload.NoticeContent, err = parseManufactureData(i+14, i+int(tempLen)-1, data, tempTLV.TLVPayload.DevMac)
			if err != nil {
				return err
			}
			tempTLV.TLVPayload.NoticeData = hex.EncodeToString(append(data[:0:0], data[i+14:i+int(tempLen)-1]...))
		case packets.TLVDisconnectMsg:
			tempTLV.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[i+4:i+10]...))
			tempTLV.TLVPayload.ConnHandle = hex.EncodeToString(append(data[:0:0], data[i+10:i+12]...))    //连接句柄
			tempTLV.TLVPayload.DisConnReason = hex.EncodeToString(append(data[:0:0], data[i+12:i+14]...)) //断开事件原因
		case packets.TLVGatewayTypeMsg:
			tempTLV.TLVPayload.GwType = hex.EncodeToString(append(data[:0:0], data[i+4:i+int(tempLen)]...))
		case packets.TLVGatewaySNMsg:
			tempTLV.TLVPayload.GwSN = hex.EncodeToString(append(data[:0:0], data[i+4:i+int(tempLen)]...))
		case packets.TLVGatewayMACMsg:
			tempTLV.TLVPayload.GwMac = hex.EncodeToString(append(data[:0:0], data[i+4:i+int(tempLen)]...))
		case packets.TLVIotModuleMsg:
			tempTLV.TLVPayload.IotModuleType = hex.EncodeToString(append(data[:0:0], data[i+4:i+int(tempLen)]...))
		case packets.TLVIotModuleSNMsg:
			tempTLV.TLVPayload.IotModuleSN = hex.EncodeToString(append(data[:0:0], data[i+4:i+int(tempLen)]...))
		case packets.TLVIotModuleMACMsg:
			tempTLV.TLVPayload.IotModuleMac = hex.EncodeToString(append(data[:0:0], data[i+4:i+int(tempLen)]...))
		}
		*restore = append(*restore, tempTLV)
		i += int(tempLen)
	}
	return err
}

//`针对终端响应BLE 应答TLV建立`
//三大消息中可直接使用，响应消息调tlv内容
func GetDevResponseTLV(data []byte, index int) (res packets.TLV, err error) {
	res.TLVMsgType = hex.EncodeToString(append(data[:0:0], data[index:index+2]...))
	res.TLVLen = hex.EncodeToString(append(data[:0:0], data[index+2:index+4]...))
	tempLen, _ := strconv.ParseUint(res.TLVLen, 16, 16)
	switch res.TLVMsgType {
	case packets.TLVScanRespMsg:
		res.TLVPayload.ErrorCode = hex.EncodeToString(append(data[:0:0], data[index+4:index+6]...))
		if err = globalutils.VerificationErrorCode(res.TLVPayload.ErrorCode); err != nil {
			return
		}
		res.TLVPayload.ScanStatus = hex.EncodeToString(append(data[:0:0], data[index+6:index+7]...))
		res.TLVPayload.ReserveOne = hex.EncodeToString(append(data[:0:0], data[index+7:index+8]...))
		res.TLVPayload.ScanType = hex.EncodeToString(append(data[:0:0], data[index+8:index+9]...))
		res.TLVPayload.ScanPhys = hex.EncodeToString(append(data[:0:0], data[index+9:index+10]...))
		res.TLVPayload.ScanInterval = hex.EncodeToString(append(data[:0:0], data[index+10:index+12]...))
		res.TLVPayload.ScanWindow = hex.EncodeToString(append(data[:0:0], data[index+12:index+14]...))
		res.TLVPayload.ScanTimeout = hex.EncodeToString(append(data[:0:0], data[index+14:index+16]...))
	case packets.TLVConnectRespMsg:
		res.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[index+4:index+10]...))
		res.TLVPayload.ErrorCode = hex.EncodeToString(append(data[:0:0], data[index+10:index+12]...))
		if err = globalutils.VerificationErrorCode(res.TLVPayload.ErrorCode); err != nil {
			return
		}
		res.TLVPayload.PHY = hex.EncodeToString(append(data[:0:0], data[index+12:index+13]...))
		res.TLVPayload.ConnHandle = hex.EncodeToString(append(data[:0:0], data[index+13:index+15]...))
		res.TLVPayload.ConnInterval = hex.EncodeToString(append(data[:0:0], data[index+15:index+17]...))
		res.TLVPayload.ConnLatency = hex.EncodeToString(append(data[:0:0], data[index+19:index+19]...))
		res.TLVPayload.ConnTimeout = hex.EncodeToString(append(data[:0:0], data[index+19:index+21]...))
		res.TLVPayload.MTUSize = hex.EncodeToString(append(data[:0:0], data[index+21:index+23]...))
	case packets.TLVMainServiceRespMsg:
		res.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[index+4:index+10]...))
		res.TLVPayload.ErrorCode = hex.EncodeToString(append(data[:0:0], data[index+10:index+12]...))
		if err = globalutils.VerificationErrorCode(res.TLVPayload.ErrorCode); err != nil {
			return
		}
		res.TLVPayload.ServiceSum = hex.EncodeToString(append(data[:0:0], data[index+12:index+14]...))
		dfsForAllTLV(data, &res.TLVPayload.TLVReserve, index+14, index+int(tempLen))
	case packets.TLVMainServiceByUUIDReqMsg:
		res.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[index+4:index+10]...))
		res.TLVPayload.ErrorCode = hex.EncodeToString(append(data[:0:0], data[index+10:index+12]...))
		if err = globalutils.VerificationErrorCode(res.TLVPayload.ErrorCode); err != nil {
			return
		}
		dfsForAllTLV(data, &res.TLVPayload.TLVReserve, index+12, index+int(tempLen))
	case packets.TLVCharFindRespMsg:
		res.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[index+4:index+10]...))
		res.TLVPayload.ServiceHandle = hex.EncodeToString(append(data[:0:0], data[index+10:index+12]...)) //服务handle
		res.TLVPayload.ErrorCode = hex.EncodeToString(append(data[:0:0], data[index+12:index+14]...))
		if err = globalutils.VerificationErrorCode(res.TLVPayload.ErrorCode); err != nil {
			return
		}
		res.TLVPayload.FeatureSum = hex.EncodeToString(append(data[:0:0], data[index+14:index+16]...))
		dfsForAllTLV(data, &res.TLVPayload.TLVReserve, index+16, index+int(tempLen))
	case packets.TLVCharConfRespMsg:
		res.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[index+4:index+10]...))
		res.TLVPayload.ErrorCode = hex.EncodeToString(append(data[:0:0], data[index+10:index+12]...))
		if err = globalutils.VerificationErrorCode(res.TLVPayload.ErrorCode); err != nil {
			return
		}
		res.TLVPayload.CharHandle = hex.EncodeToString(append(data[:0:0], data[index+12:index+14]...))
		res.TLVPayload.CCCDHandle = hex.EncodeToString(append(data[:0:0], data[index+14:index+16]...))
		res.TLVPayload.CCCDHandleValue = hex.EncodeToString(append(data[:0:0], data[index+16:index+17]...)) //特征配置
	case packets.TLVCharOptRespMsg:
		res.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[index+4:index+10]...))
		res.TLVPayload.ErrorCode = hex.EncodeToString(append(data[:0:0], data[index+10:index+12]...))
		if err = globalutils.VerificationErrorCode(res.TLVPayload.ErrorCode); err != nil {
			return
		}
		res.TLVPayload.ParaLength = hex.EncodeToString(append(data[:0:0], data[index+12:index+14]...))
		res.TLVPayload.CharHandle = hex.EncodeToString(append(data[:0:0], data[index+14:index+16]...))
		res.TLVPayload.ReserveOne = hex.EncodeToString(append(data[:0:0], data[index+16:index+18]...))
		res.TLVPayload.ParaValue = hex.EncodeToString(append(data[:0:0], data[index+18:]...))
	case packets.TLVMainServiceByUUIDRespMsg:
		res.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[index+4:index+10]...))
		res.TLVPayload.ErrorCode = hex.EncodeToString(append(data[:0:0], data[index+10:index+12]...))
		if err = globalutils.VerificationErrorCode(res.TLVPayload.ErrorCode); err != nil {
			return
		}
		dfsForAllTLV(data, &res.TLVPayload.TLVReserve, index+12, index+int(tempLen))
	case packets.TLVDeviceListMsg:
		res.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[index+4:index+10]...))
		res.TLVPayload.ConnHandle = hex.EncodeToString(append(data[:0:0], data[index+10:index+12]...))
	case packets.TLVNotifyMsg:
		res.TLVPayload.DevMac = hex.EncodeToString(append(data[:0:0], data[index+4:index+10]...))
		res.TLVPayload.ReserveOne = hex.EncodeToString(append(data[:0:0], data[index+10:index+11]...))
		res.TLVPayload.NoticeType = hex.EncodeToString(append(data[:0:0], data[index+11:index+12]...))
		res.TLVPayload.CharHandle = hex.EncodeToString(append(data[:0:0], data[index+12:index+14]...))
		res.TLVPayload.CharHandleValue = hex.EncodeToString(append(data[:0:0], data[index+14:]...))
	default:
		return packets.TLV{}, errors.New("<GetDevResponseTLV>: the corresponding message type cannot be recognized")
	}
	return
}

//处理上行消息时消息弹栈操作
//校验消息是否与当前消息对应
//正对消息、落后消息，超前不存在
// 网关不需要
// -1 出现错误 0 正对  1 落后 2超前不存在

//frameKey 表终端mac
func CheckUpDevMessageFrame(ctx context.Context, devCacheKey, devSN string) (resCode int) {
	devMarkKey := globalutils.CreateCacheKey(devCacheKey, devSN)
	resCode = globalconstants.DEV_CORRECT
	var (
		frameInfo storage.NodeCache
		cacheStr  string
		cache     []byte
		queue     interface{}
		err       error
		ok        bool
	)
	if config.C.General.UseRedis {
		cacheStr, err = globalredis.RedisCache.LIndex(ctx, devCacheKey, 0).Result()
		if err != nil && err != redis.Nil {
			resCode = globalconstants.DEV_ERROR
			if err != redis.Nil {
				resCode = globalconstants.DEV_EXCEPTION
				globallogger.Log.Errorf("<CheckUpDevMessageFrame> [DevEui: %s] get redis error %v", devCacheKey, err)
				return
			}
			globallogger.Log.Errorf("<CheckUpGwMessageFrame> [DevEui: %s] get gwCacheKey has loss %v", devCacheKey, err)
			return
		}
		cache = []byte(cacheStr)
		err = json.Unmarshal([]byte(cache), &frameInfo)
		if err != nil {
			globallogger.Log.Errorf("<CheckUpDevMessageFrame> [DevEui: %s] data change error %v", devCacheKey, err)
			resCode = globalconstants.DEV_EXCEPTION
			return
		}
	} else {
		queue, ok = globalmemo.MemoCacheDev.Get(devCacheKey)
		if !ok {
			globallogger.Log.Errorf("<CheckUpDevMessageFrame> [DevEui: %s] has no cache %v", devCacheKey, err)
			resCode = globalconstants.DEV_EXCEPTION
			return
		}
		frameInfo = queue.(*storage.CQueue).Peek()
	}
	if frameInfo.FirstMark != devMarkKey || globalutils.CompareTimeIsExpire(frameInfo.TimeStamp, globalconstants.DevMessageLimitTime) { //数据不一致 时间过期
		globallogger.Log.Errorf("<CheckUpDevMessageFrame> [DevEui: %s] cache is out", devCacheKey)
		resCode = globalconstants.DEV_ERROR
	} else { //数据校验正确
		if config.C.General.UseRedis {
			globalredis.RedisCache.LPop(ctx, devCacheKey)
		} else {
			queue.(*storage.CQueue).Dequeue()
		}
	}
	return
}

//网关消息校验
func CheckUpGwMessageFrame(ctx context.Context, gwCacheKey string) (resCode int) {
	resCode = globalconstants.DEV_CORRECT
	var pastTime time.Time
	if config.C.General.UseRedis {
		strTime, err := globalredis.RedisCache.Get(ctx, gwCacheKey).Result()
		if err != nil {
			resCode = globalconstants.DEV_ERROR
			if err != redis.Nil {
				resCode = globalconstants.DEV_EXCEPTION
				globallogger.Log.Errorf("<CheckUpGwMessageFrame> [DevEui: %s] redis has error %v", gwCacheKey, err)
				return
			}
			globallogger.Log.Errorf("<CheckUpGwMessageFrame> [DevEui: %s] get gwCacheKey has loss %v", gwCacheKey, err)
			return
		}
		pastTime, err1 := time.Parse(time.RFC3339, strTime)
		if err1 != nil {
			globallogger.Log.Errorf("<CheckUpGwMessageFrame> [DevEui: %s] get timeFormat has error %v", gwCacheKey, err)
			resCode = globalconstants.DEV_EXCEPTION
			return
		}
		if globalutils.CompareTimeIsExpire(pastTime, globalconstants.GwMessageLimitTime) {
			globallogger.Log.Errorf("<CheckUpGwMessageFrame> [DevEui: %s] message is timeout %v", gwCacheKey, err)
			_, err1 = globalredis.RedisCache.Del(ctx, gwCacheKey).Result()
			if err1 != nil {
				globallogger.Log.Errorf("<CheckUpGwMessageFrame> [DevEui: %s] get redis has error %v", gwCacheKey, err1)
				resCode = globalconstants.DEV_ERROR
				return
			}
		}
		globalredis.RedisCache.Del(ctx, gwCacheKey)
	} else {
		times, exist := globalmemo.MemoCacheGw.Get(gwCacheKey)
		if !exist {
			globallogger.Log.Errorf("<CheckUpGwMessageFrame> [DevEui: %s] get gwCacheKey has loss", gwCacheKey)
			resCode = globalconstants.DEV_ERROR
			return
		}
		pastTime = times.(time.Time)
		if globalutils.CompareTimeIsExpire(pastTime, globalconstants.GwMessageLimitTime) {
			globallogger.Log.Errorf("<CheckUpGwMessageFrame> [DevEui: %s] get gwCacheKey has timeout", gwCacheKey)
		}
		globalmemo.MemoCacheGw.Remove(gwCacheKey)
	}
	return
}

//通用处理网关应答 | 终端确认
func DealWithConfirm(ctx context.Context, linkSN, appSN, methodName, moduleId, gwMac string) bool {
	globallogger.Log.Infof("<%s> [DevEui:%s] start deal response message", methodName, gwMac)
	var (
		curLinkSN, curAppSN             int64
		verifyLinkFrame, verifyAppFrame int
	)
	if appSN != "" {
		verifyAppFrame = CheckUpDevMessageFrame(ctx, globalutils.CreateCacheKey(globalconstants.BleDevCacheMessagePrefix, gwMac, moduleId), appSN)
		if verifyAppFrame != globalconstants.DEV_CORRECT {
			globallogger.Log.Errorf("<%s> [DevEui: %s] has an frame is error verifyAppFrame = %v", methodName, gwMac+moduleId, curAppSN)
			return false
		}
		return true
	}
	curLinkSN, _ = strconv.ParseInt(linkSN, 16, 32) //网关键
	verifyLinkFrame = CheckUpGwMessageFrame(ctx, globalutils.CreateCacheKey(globalconstants.BleGwDevCacheMessagePrefix, gwMac, linkSN))
	if verifyLinkFrame != globalconstants.DEV_CORRECT {
		globallogger.Log.Errorf("<%s> [DevEui: %s] has an frame is error verifyLinkFrame = %v", methodName, gwMac, curLinkSN)
		return false
	}
	return true
}

//处理应答消息, 确认消息会带上帧号一起存储
func DealWithBleResponse(ctx context.Context, needRespFrame, gwMac, moduleId string) bool {
	cacheKeyResp := globalutils.CreateCacheKey(globalconstants.BleDevCacheMessageResponsePrefix, gwMac, moduleId, needRespFrame)
	var err error
	if config.C.General.UseRedis {
		_, err = globalredis.RedisCache.Get(ctx, cacheKeyResp).Result()
		if err != nil { //缓存获取失败就不会继续处理
			if err != redis.Nil {
				globallogger.Log.Errorf("<DealWithBleResponse> redis has error %v", err)
			}
			return false
		}
		globalredis.RedisCache.Del(ctx, cacheKeyResp)
	} else {
		_, err := globalmemo.BleFreeCacheDevResponseInfo.Get([]byte(cacheKeyResp))
		if err != nil {
			return false
		}
		globalmemo.BleFreeCacheDevResponseInfo.Del([]byte(cacheKeyResp))
	}
	return true
}

//解析厂商数据
//后续按照adType、以及公司情况及进行过滤
//非01-04消息后续探究
func parseManufactureData(index, limit int, data []byte, devEui string) (manuRes packets.ManuFactContent, err error) {
	globallogger.Log.Debugf("<parseManufactureData> [DevEui: %s] start deal manufacture data", devEui)
	manuRes.Flags.Len = hex.EncodeToString(append(data[:0:0], data[index:index+1]...))
	flagLen, _ := strconv.ParseUint(manuRes.Flags.Len, 16, 8)
	if flagLen != 2 {
		return packets.ManuFactContent{}, errors.New("non-standard flag len format")
	}
	manuRes.Flags.ADType = hex.EncodeToString(append(data[:0:0], data[index+1:index+2]...))
	manuRes.Flags.ADData = hex.EncodeToString(append(data[:0:0], data[index+2:index+3]...))
	if manuRes.Flags.ADType != "01" || manuRes.Flags.ADData != "06" {
		return packets.ManuFactContent{}, errors.New("non-standard data format")
	}
	manuRes.ManuData.Length = hex.EncodeToString(append(data[:0:0], data[index+3:index+4]...))
	manuRes.ManuData.Data.AdType = hex.EncodeToString(append(data[:0:0], data[index+4:index+5]...))
	if manuRes.ManuData.Data.AdType != "ff" {
		return packets.ManuFactContent{}, errors.New("non-standard Ble data format")
	}
	manuRes.ManuData.Data.AdData.CompanyID = hex.EncodeToString(append(data[:0:0], data[index+5:index+7]...))
	if manuRes.ManuData.Data.AdData.CompanyID != globalconstants.ManufactureH3CCompany {
		return packets.ManuFactContent{}, errors.New("non-standard H3C data format")
	}
	manuRes.ManuData.Data.AdData.CompData.MsgType = hex.EncodeToString(append(data[:0:0], data[index+7:index+9]...))
	switch manuRes.ManuData.Data.AdData.CompData.MsgType {
	case "0101", "0103":
		manuRes.ManuData.Data.AdData.CompData.DeviceData = hex.EncodeToString(append(data[:0:0], data[index+9:limit]...))
	case "0102", "0104":
		manuRes.ManuData.Data.AdData.CompData.Device.DevType = hex.EncodeToString(append(data[:0:0], data[index+9:index+11]...))
		manuRes.ManuData.Data.AdData.CompData.Device.Addr = hex.EncodeToString(append(data[:0:0], data[index+11:index+17]...))
		if limit > index+17 {
			manuRes.ManuData.Data.AdData.CompData.DeviceData = hex.EncodeToString(append(data[:0:0], data[index+17:limit]...))
		}
	}
	return
}

//查验网关链路是否存在
func CheckGatewayInfo(ctx context.Context, gwMac string) bool {
	globallogger.Log.Debugf("<CheckGatewayInfo> [GwDevEui: %s] start check linked", gwMac)
	cacheKey := globalutils.CreateCacheKey(globalconstants.GwSocketCachePrefix, gwMac)
	if config.C.General.UseRedis {
		_, err := globalredis.RedisCache.Get(ctx, cacheKey).Result()
		if err != nil {
			globallogger.Log.Errorf("<CheckGatewayInfo> [GwDevEui: %s] no linked", gwMac)
			return false
		}
	} else {
		_, err := globalmemo.SocketFreeCache.Get([]byte(cacheKey))
		if err != nil {
			globallogger.Log.Errorf("<CheckGatewayInfo> [GwDevEui: %s] no linked", gwMac)
			return false
		}
	}
	return true
}
